import {
  SceneSetup,
  pickingData,
  MouseStyle,
  CMEVENTS,
  coreObserver,
  PickerState,
  Types,
} from '@amcax/base';
import { Vector3 } from 'three';
import { Picker3D, PickerMgr } from '@amcax/renderer';
import { KEYBOARD_KEY, preventKeys } from '../Constants';
import { ctx2, userEvents } from '../../module';

// 事件定义
export const CENTEREVENTS = {
  POINTERMOVE: 'pointermove',
  POINTERDOWN: 'pointerdown',
  POINTERUP: 'pointerup',
  // POINTEROVER: 'pointerover',
  // POINTEROUT: 'pointerout',
  // POINTERENTER: 'pointerenter',
  // POINTERLEAVE: 'pointerleave',
  CLICK: 'click',
  DBCLICK: 'dblclick',
  KEYUP: 'keyup',
  KEYDOWN: 'keydown',
  KEYPRESS: 'keypress',
};

export type MouseInfo = {
  x: number;
  y: number;
};

export type Button = {
  onLeft: boolean;
  onMiddle: boolean;
  onRight: boolean;
  buttonDown: boolean;
};
export enum STATE {
  DEFAULT = 'default', // 建模
  SLEEPING = 'sleeping', // 禁用状态
}

export enum PICKTYPE {
  MODEL = 1, // 体
  FACE = 2, // 面
  LINE = 3, // 线
}

export const pickSetting = {
  1: { model: true },
  2: { face: true },
  3: { line: true },
};

export type EventsRes = {
  eventName: string;
  button?: Button;
  mouse?: MouseInfo;
  mouseWorld?: Vector3;
  key?: string;
  idsMap?: Map<number, number>;
  event: MouseEvent | PointerEvent | KeyboardEvent;
};

export const getPointerButton = (event: PointerEvent): Button => {
  if (event.button === -1) {
    // pointermove状态
    return {
      onLeft: event.buttons == 1,
      onMiddle: event.buttons == 4,
      onRight: event.buttons == 2,
      buttonDown:
        event.buttons == 1 || event.buttons == 4 || event.buttons == 2,
    };
  } else {
    // pointerdown / pointerup状态
    return {
      onLeft: event.button == 0,
      onMiddle: event.button == 1,
      onRight: event.button == 2,
      buttonDown: event.button == 0 || event.button == 1 || event.button == 2,
    };
  }
};

let editingGroup: boolean; //编辑组

/* 事件模块 */
export class EventCenter extends coreObserver {
  private _events: object; // 事件集合
  pickType: PICKTYPE; // 拾取类型（线、面、体）
  state: string; // 事件状态
  sceneSetup: SceneSetup;
  canvas: HTMLElement;
  pickingData: pickingData;
  idsMap: Map<number, number>;
  mouse: MouseInfo; // 鼠标信息
  pickedHoleId: string; // 定位中的洞口id
  static instance: EventCenter;

  constructor() {
    super();
    this.sceneSetup = ctx2.viewer.sceneSetup;
    this.canvas = this.sceneSetup.container;
    this.pickingData = this.sceneSetup.pickingData;
    this.state = STATE.SLEEPING;
    this.pickType = PICKTYPE.MODEL;
    this.mouse = {
      x: -10000,
      y: -10000,
    };
    this.init();
  }
  delete(): void {
    return null;
  }

  static getInstance(): EventCenter {
    if (!this.instance) {
      this.instance = new EventCenter();
    }
    return this.instance;
  }

  init() {
    if (this.state != STATE.SLEEPING) return;
    this.bindEvent(CENTEREVENTS.POINTERDOWN, this.canvasPointerdown.bind(this));
    this.bindEvent(CENTEREVENTS.POINTERUP, this.canvasPointerup.bind(this));
    this.bindEvent(CENTEREVENTS.POINTERMOVE, this.canvasPointermove.bind(this));
    this.bindEvent(CENTEREVENTS.DBCLICK, this.canvasDblClick.bind(this));
    this.bindEvent(CENTEREVENTS.CLICK, this.canvasClick.bind(this));
    // offCanvasEvent
    this.bindDocumentEvent(
      CENTEREVENTS.POINTERUP,
      this.offCanvasPointerUp.bind(this),
    );
    this.state = STATE.DEFAULT;
    /** key event */
    this.bindDocumentEvent(CENTEREVENTS.KEYUP, this.keyUp.bind(this));
    this.bindDocumentEvent(CENTEREVENTS.KEYDOWN, this.keyDown.bind(this));
    this.bindDocumentEvent(CENTEREVENTS.KEYPRESS, this.keyPress.bind(this));
  }

  // 暂时关闭事件中心
  sleep() {
    this.offEvent(CENTEREVENTS.POINTERDOWN);
    this.offEvent(CENTEREVENTS.POINTERUP);
    this.offEvent(CENTEREVENTS.POINTERMOVE);
    this.offEvent(CENTEREVENTS.DBCLICK);
    this.offEvent(CENTEREVENTS.CLICK);
    // offcanvas
    this.offDocumentEvent(CENTEREVENTS.POINTERUP);
    this.offDocumentEvent(CENTEREVENTS.KEYDOWN);
    this.offDocumentEvent(CENTEREVENTS.KEYUP);
    this.offDocumentEvent(CENTEREVENTS.KEYPRESS);
    this.state = STATE.SLEEPING;
    this.listeners.clear();
  }

  // 修改state
  setState(state: string) {
    if (state != STATE.DEFAULT)
      PickerMgr.getPicker<Picker3D>('3d').setState(PickerState.onCommand);
    // console.log('set state', state)
    this.touchExit();
    this.state = state;
    if (state === 'editGroup') editingGroup = true;
    return this;
  }
  // 恢复默认
  setDefault(force: boolean = false) {
    PickerMgr.getPicker<Picker3D>('3d').setState(PickerState.default);
    if (force) {
      this.state = STATE.DEFAULT;
      editingGroup = false;
    } else {
      if (editingGroup) {
        this.state = 'editGroup';
      } else {
        this.state = STATE.DEFAULT;
      }
    }
    this.clearExit();
    return this;
  }

  // 设置拾取类型
  setPickType(type: number) {
    if (this.pickType !== type) {
      this.pickType = type;
      PickerMgr.getPicker<Picker3D>('3d').clickNull();
      PickerMgr.getPicker<Picker3D>('3d').setGlobalPickType(type);
    }
    return this;
  }

  // 鼠标双击
  canvasDblClick(event: MouseEvent) {
    // 建模模式
    if (this.state === STATE.DEFAULT) {
      // 判断是否可以进入组编辑
      // if (!ctx?.interactionData?.cmdParams?.groupingActive) {
      //   initEditGroup()
      // }
    }
    const ev: EventsRes = {
      eventName: CENTEREVENTS.DBCLICK,
      idsMap: this.idsMap,
      mouse: this.mouse,
      event,
    };
    this.emit(CENTEREVENTS.DBCLICK, ev);
  }

  // 鼠标单击
  canvasClick(event: MouseEvent) {
    const ev: EventsRes = {
      eventName: CENTEREVENTS.DBCLICK,
      idsMap: this.idsMap,
      mouse: this.mouse,
      event,
    };
    this.emit(CENTEREVENTS.CLICK, ev);
  }

  canvasPointerdown(event: PointerEvent) {
    const button = getPointerButton(event);
    const ev: EventsRes = {
      eventName: CENTEREVENTS.POINTERDOWN,
      button,
      mouse: this.mouse,
      idsMap: this.idsMap,
      event,
    };
    PickerMgr.getPicker<Picker3D>('3d').setPointerDownPos(ev);
    this.emit(CENTEREVENTS.POINTERDOWN, ev);
  }

  canvasPointerup(event: PointerEvent) {
    const button = getPointerButton(event);
    const ev: EventsRes = {
      eventName: CENTEREVENTS.POINTERUP,
      button,
      idsMap: this.idsMap,
      mouse: this.mouse,
      event,
    };
    // 默认状态下支持多选和单击空白处清空

    // 鼠标左键和右键都可以选中模型，点击右键选中的模型后可显示上下文菜单(上下文菜单的逻辑见：@app-cad/components/ContextMenu)
    if (this.state === STATE.DEFAULT && (button.onLeft || button.onRight)) {
      if (this.idsMap && this.idsMap.size > 0) {
        // PickerMgr.getPicker<Picker3D>('3d').selectModel(event)
        console.log('选中模型');
        PickerMgr.getPicker<Picker3D>('3d').select(event);
        // 默认状态下支持洞口十字定位
        const modelData = PickerMgr.getPicker<Picker3D>(
          '3d',
        ).getCurrentModel() as any;
        // console.log('modelData', modelData)
        if (
          modelData &&
          (modelData.objInfo?.type === Types.HOLE ||
            modelData.objInfo?.type === 'opening') &&
          modelData.objInfo.parent != undefined
        ) {
          console.log('进入洞口定位', modelData);
          // HoleLocator().do(modelData)
          this.pickedHoleId = modelData.idInfo.o;
        } else {
          // HoleLocator().distroy()
          if (this.pickedHoleId) {
            PickerMgr.getPicker<Picker3D>('3d').uncheckModels([
              this.pickedHoleId,
            ]);
            const children = modelData?.objInfo?.child;
            if (children) {
              for (let i = 0, l = children.length; i < l; i++) {
                if (children[i] === this.pickedHoleId) {
                  PickerMgr.getPicker<Picker3D>('3d').selectModels([
                    modelData.idInfo.o,
                  ]);
                }
              }
            }
            // if (modelData?.idInfo?.child === this.pickedHoleId) {
            //   PickerMgr.getPicker<Picker3D>('3d').selectModels([modelData.idInfo.o])
            // }
          }
          this.pickedHoleId = null;
        }
      } else {
        // 鼠标右键不取消选中模型
        if (!button.onRight) {
          // HoleLocator().distroy()
          PickerMgr.getPicker<Picker3D>('3d').clickNull();
        }
      }
      PickerMgr.getPicker<Picker3D>('3d').marqueePointerUp();

      // 显示右键菜单
      if (button.onRight) {
        userEvents.emit(CMEVENTS.MODEL_RIGHT_CLICK, event);
      }
    }
    //111111111111111111111111111111111111

    // }
    this.emit(CENTEREVENTS.POINTERUP, ev);
  }

  /**
   *
   * pointermove 事件
   */
  canvasPointermove(event: PointerEvent) {
    // todo
    const button = getPointerButton(event);
    let rect = this.canvas.getBoundingClientRect();
    this.mouse = {
      x: Math.round(event.clientX - rect.left),
      y: Math.round(event.clientY - rect.top),
    };
    this.idsMap = null;
    // 拾取
    if (this.pickingData.pixelBuffer && !button.buttonDown) {
      this.idsMap = this.mouseMoving(this.mouse);
    }
    const ev: EventsRes = {
      eventName: CENTEREVENTS.POINTERMOVE,
      button,
      idsMap: this.idsMap,
      mouse: this.mouse, // 实际鼠标屏幕坐标
      event,
    };
    // 默认状态下支持选择体
    if (this.state === STATE.DEFAULT && !button.buttonDown) {
      MouseStyle.setName('');
      const pickSet = pickSetting[this.pickType];
      PickerMgr.getPicker<Picker3D>('3d').pick(ev, pickSet);
    }
    // 框选功能
    if (
      this.state === STATE.DEFAULT &&
      button.onLeft &&
      this.pickType === PICKTYPE.MODEL
    ) {
      PickerMgr.getPicker<Picker3D>('3d').marqueeSelection(ev);
    }
    // }
    this.emit(CENTEREVENTS.POINTERMOVE, ev);
  }
  mouseMoving(mouse: any) {
    let mouseX = mouse.x;
    let mouseY = mouse.y;
    const idMap = new Map();
    const setId = (id: number) => {
      if (!idMap.has(id)) {
        idMap.set(id, 1);
      } else {
        let t = idMap.get(id);
        idMap.set(id, t + 1);
      }
    };
    for (let x = mouseX - 2; x < mouseX + 2; x++) {
      for (let y = mouseY - 2; y < mouseY + 2; y++) {
        let i =
          Math.round(
            x + (this.canvas.clientHeight - y) * this.canvas.clientWidth,
          ) * 4; // 获取像素点index
        let id =
          (this.pickingData.pixelBuffer[i] << 16) |
          (this.pickingData.pixelBuffer[i + 1] << 8) |
          this.pickingData.pixelBuffer[i + 2];
        if (id != 0) {
          setId(id);
        }
      }
    }
    return idMap.size > 0 ? idMap : null;
  }

  //*****pointerMove end */
  /**
   *
   * offCavasEvent
   */
  offCanvasPointerUp(event: PointerEvent) {
    if (event.target === this.sceneSetup.domElement().childNodes[1]) return;
    event.preventDefault();
    event.stopPropagation();
    // 结束框选
    PickerMgr.getPicker<Picker3D>('3d').marqueePointerUp();
    return;
  }

  /**
   *
   * 键盘事件
   */

  keyUp(event: KeyboardEvent) {
    if (preventKeys.indexOf(event.key) != -1) {
      event.preventDefault();
    }
    if (
      event.key === KEYBOARD_KEY.DELETE ||
      event.key === KEYBOARD_KEY.BACKSPACE
    ) {
      if (this.state === STATE.DEFAULT && event.target == document.body) {
        // deleteModels()
      }
    }
    // deleteModels()
    // 进入键盘事件
    const ev: EventsRes = {
      eventName: CENTEREVENTS.KEYUP,
      event,
      key: event.key,
    };
    this.emit(CENTEREVENTS.KEYUP, ev);
  }

  keyDown(event: KeyboardEvent) {
    if (preventKeys.indexOf(event.key) != -1) {
      event.preventDefault();
    }
    // 进入键盘事件
    const ev: EventsRes = {
      eventName: CENTEREVENTS.KEYDOWN,
      event,
      key: event.key,
    };
    this.emit(CENTEREVENTS.KEYDOWN, ev);
  }
  keyPress(event: KeyboardEvent) {
    if (preventKeys.indexOf(event.key) != -1) {
      event.preventDefault();
    }
    // 进入键盘事件
    const ev = {
      eventName: CENTEREVENTS.KEYPRESS,
      event,
      key: event.key,
    };
    this.emit(CENTEREVENTS.KEYPRESS, ev);
  }

  //事件监听

  bindDocumentEvent(name: string, callback: Function) {
    // 不同设备特殊处理 todo
    let wrappedCallback = (event) => {
      callback(event);
    };
    this._events = this._events || {};
    this._events[name] = wrappedCallback;
    document.addEventListener(name, wrappedCallback);
  }
  offDocumentEvent(name: string) {
    // 不同设备特殊处理 todo
    if (this._events && this._events[name]) {
      document.removeEventListener(name, this._events[name]);
      delete this._events[name];
    }
  }

  bindEvent(name: string, callback: Function) {
    // 不同设备特殊处理 todo
    let wrappedCallback = (event) => {
      callback(event);
    };
    this._events = this._events || {};
    this._events[name] = wrappedCallback;
    this.canvas.addEventListener(name, wrappedCallback);
  }
  offEvent(name: string) {
    // 不同设备特殊处理 todo
    if (this._events && this._events[name]) {
      this.canvas.removeEventListener(name, this._events[name]);
      delete this._events[name];
    }
  }
}

export const csEvent = () => {
  return EventCenter.getInstance();
};
